package com.gjy.util;

import cn.hutool.core.util.StrUtil;
import com.gjy.comment.AjaxResult;
import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

import static com.google.code.kaptcha.Constants.*;

/**
 * 二维码
 * 图形计算验证码
 *
 * @author 宫静雨
 * @version 1.0
 * @since 2023-01-30 16:07:38
 */
public class QrCodeUtil {

    private static final Logger logger = LoggerFactory.getLogger(QrCodeUtil.class);

    @Data
    private static final class R {
        private String img;

        private String uuid;
    }

    private final static Producer captchaProducerMath = getKaptchaBeanMath();

    /**
     * 图形计算验证码 x+y=?
     */
    public static AjaxResult getCaptcha() {
        ByteArrayOutputStream bs = null;
        String imgSrc;
        R response = new R();
        String key = UUID.randomUUID().toString();
        try {
            String capStr;
            String code;
            BufferedImage bi;
            String capText = captchaProducerMath.createText();
            capStr = capText.substring(0, capText.lastIndexOf("@"));
            code = capText.substring(capText.lastIndexOf("@") + 1);
            bi = captchaProducerMath.createImage(capStr);
            // 存入redis并设置过期时间为30分钟
//            redisRepository.setExpire(key, code, 300);
            logger.info("code = {}", code);
            bs = new ByteArrayOutputStream();
            ImageIO.write(bi, "jpg", bs);
            imgSrc = Base64.getEncoder().encodeToString(bs.toByteArray());
            bs.flush();
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("图片验证码生成-接口异常", e);
            return AjaxResult.error("图片验证码生成失败");
        } finally {
            try {
                Objects.requireNonNull(bs).close();
            } catch (IOException e) {
                logger.error("图片验证码生成-接口异常", e);
                e.printStackTrace();
            }
        }
        response.setImg(imgSrc);
        response.setUuid(key);
        logger.info("imgSrc = {}", imgSrc);
        logger.info("key = {}", key);
        return AjaxResult.ok(response);
    }

    private static DefaultKaptcha getKaptchaBeanMath() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        // 是否有边框 默认为true 我们可以自己设置yes，no
        properties.setProperty(KAPTCHA_BORDER, "yes");
        // 边框颜色 默认为Color.BLACK
        properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
        // 验证码文本字符颜色 默认为Color.BLACK
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
        // 验证码图片宽度 默认为200
        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
        // 验证码图片高度 默认为50
        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
        // 验证码文本字符大小 默认为40
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
        // KAPTCHA_SESSION_KEY
        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
        // 验证码文本生成器
        properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL,
                "com.gjy.util.KaptchaTextCreator");
        // 验证码文本字符间距 默认为2
        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
        // 验证码文本字符长度 默认为5
        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
        // 验证码噪点颜色 默认为Color.BLACK
        properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
        // 干扰实现类
        properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
        properties
                .setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }

    /**
     * 二维码颜色
     */
    private static final int QRCOLOR = 0xFF000000;

    /**
     * 背景色
     */
    private static final int BGCOLOR = 0xFFFFFFFF;

    /**
     * 二维码宽度
     */
    private static final int WIDTH = 400;

    /**
     * 二维码高度
     */
    private static final int HEIGHT = 400;

    /**
     * 设置二维码的格式参数
     */
    private static Map<EncodeHintType, Object> getDecodeHintType() {
        // 用于设置QR二维码参数
        Map<EncodeHintType, Object> hints = new HashMap<>(16);
        // 设置QR二维码的纠错级别（H为最高级别）具体级别信息
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 设置编码方式
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        //设置二维码四周空白部分
        hints.put(EncodeHintType.MARGIN, 2);
        return hints;
    }

    /**
     * 宋体文字获取
     *
     * @param style 字体样式（加粗，斜体等）
     * @param size  字体大小
     */
    public static Font getSimSun(int style, int size) {
        logger.info("===加载宋体字体===");
        Font font = new Font("宋体", style, size);
        //获取字体格式文件的流
        try (InputStream simSunFontFile = new ClassPathResource("fonts/simsun.ttf").getInputStream()) {
            //创建字体
            font = Font.createFont(Font.PLAIN, simSunFontFile).deriveFont(style, size);
        } catch (Exception e) {
            logger.error("字体生成失败", e);
        }
        return font;
    }

    /**
     * 普通二维码生成
     */
    private static BufferedImage createCode(String content) {
        MultiFormatWriter multiFormatWriter;
        BitMatrix bm;
        BufferedImage image = null;
        Map<EncodeHintType, Object> hints = getDecodeHintType();
        try {
            multiFormatWriter = new MultiFormatWriter();
            // 参数顺序分别为：编码内容，编码类型，生成图片宽度，生成图片高度，设置参数
            bm = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, WIDTH, HEIGHT, hints);
            int w = bm.getWidth();
            int h = bm.getHeight();
            image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);

            // 开始利用二维码数据创建Bitmap图片，分别设为黑（0xFFFFFFFF）白（0xFF000000）两色
            for (int x = 0; x < w; x++) {
                for (int y = 0; y < h; y++) {
                    image.setRGB(x, y, bm.get(x, y) ? QRCOLOR : BGCOLOR);
                }
            }
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return image;
    }

    /**
     * 创建带底部文字的二维码图片
     */
    private static BufferedImage createCodeWithText(String content, String text) {
        BufferedImage image = createCode(content);
        //把文字添加上去，文字不要太长，这里最多支持两行。太长就会自动截取啦
        try {
            if (StrUtil.isNotEmpty(text)) {
                //新的图片，把带logo的二维码下面加上文字
                BufferedImage outImage = new BufferedImage(400, 445, BufferedImage.TYPE_4BYTE_ABGR);
                Graphics2D outg = outImage.createGraphics();
                outg.setBackground(Color.WHITE);
                outg.clearRect(0, 0, 400, 445);
                //画二维码到新的面板
                outg.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
                // 防止生成的文字带有锯齿
                outg.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

                //画文字到新的面板
                outg.setColor(Color.BLACK);
                //字体、字型、字号
                outg.setFont(getSimSun(Font.BOLD, 30));

                int strWidth = outg.getFontMetrics().stringWidth(text);
                if (strWidth >= WIDTH) {
                    //长度过长就换行
                    String productName1 = text.substring(0, text.length() / 2);
                    String productName2 = text.substring(text.length() / 2);
                    int strWidth1 = outg.getFontMetrics().stringWidth(productName1);
                    int strWidth2 = outg.getFontMetrics().stringWidth(productName2);
                    outg.drawString(productName1, 200 - strWidth1 / 2, image.getHeight() + (outImage.getHeight() - image.getHeight()) / 2 + 12);
                    BufferedImage outImage2 = new BufferedImage(400, 485, BufferedImage.TYPE_4BYTE_ABGR);
                    Graphics2D outg2 = outImage2.createGraphics();
                    outg2.setBackground(Color.WHITE);
                    outg2.clearRect(0, 0, 400, 485);
                    outg2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
                    outg2.drawImage(outImage, 0, 0, outImage.getWidth(), outImage.getHeight(), null);
                    outg2.setColor(Color.BLACK);
                    // 字体、字型、字号
                    outg2.setFont(getSimSun(Font.BOLD, 30));
                    outg2.drawString(productName2, 200 - strWidth2 / 2, outImage.getHeight() + (outImage2.getHeight() - outImage.getHeight()) / 2 + 5);
                    outg2.dispose();
                    outImage2.flush();
                    outImage = outImage2;
                } else {
                    //画文字
                    outg.drawString(text, 200 - strWidth / 2, image.getHeight() + (outImage.getHeight() - image.getHeight()) / 2 + 12);
                }
                outg.dispose();
                outImage.flush();
                image = outImage;
                image.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return image;
    }

    /**
     * 获取带有文字的二维码 字节数组
     *
     * @param content 二维码内容
     * @param note    底部文字内容
     * @return 二维码字节数组
     */
    public static byte[] getQrCodeWithText(String content, String note) throws IOException {
        BufferedImage bufferedImage = createCodeWithText(content, note);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write(bufferedImage, "png", out);
        return out.toByteArray();
    }

    public static void main(String[] args) throws IOException {
        //支持 26个字
        BufferedImage image = QrCodeUtil.createCodeWithText("保育舍1栋2单元", "保育舍1栋2单元");
        ImageIO.write(image, "png", new File("a.png"));
    }

}
